package gamepad;
import java.io.IOException;
import java.io.Serializable;
import java.util.Timer;
import java.util.TimerTask;

import mobilefeel.AndroidDevice;
import mobilefeel.NotConnectedException;
import mobilefeel.accelerometer.Accelerometer;
import mobilefeel.accelerometer.AccelerometerListener;
import mobilefeel.dto.AccelerometerEvent;
import mobilefeel.dto.TouchEvent;
import mobilefeel.touchscreen.OnTouchListener;
import mobilefeel.touchscreen.View;
import mobilefeel.touchscreen.ViewListener;

import org.flypad.joystick.Joystick;
import org.flypad.joystick.JoystickException;



public class GamePad implements OnTouchListener, ViewListener, AccelerometerListener {

	private AndroidDevice device;
	private String modo;
	private Joystick joystick;
	private int joystickNum;
	private Thread updateThread;
	private View acelerador, freno, arriba, abajo;
	private View[] botones = new View[10];
	
	private int updateInterval;
	private int anguloLimite;
	private float limiteX;
	private float limiteY;
	private float deadzoneX;
	private float deadzoneY;
	
	public static int UPDATE_INTERVAL = 25;
	public static int ANGULO_LIMITE = 160;
	public static float LIMITE_X = 6F;
	public static float LIMITE_Y = 5F;
	public static float DEADZONE_X = 0.3F;
	public static float DEADZONE_Y = 0.25F;
	
	private float factorX;
	private float factorY;
	private float factorAngular;
	private double anguloAnterior;
	
	public GamePad(AndroidDevice ad, int num) throws JoystickException{
		device = ad;
		joystickNum = num;
		
		modo = "";
		anguloAnterior = 0;
		
		setUpdateInterval(UPDATE_INTERVAL);
		setDeadzones(DEADZONE_X, DEADZONE_Y);
		setLimites(LIMITE_X, LIMITE_Y);
		setAnguloLimite(ANGULO_LIMITE);
		
		device.getTouchScreen().addViewListener(this);
		
		joystick = new Joystick("\\\\.\\PPJoyIOCTL" + joystickNum);
		
		updateThread = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					while (!Thread.interrupted()) {
						joystick.send();
						Thread.sleep(updateInterval);
					}
				} catch (InterruptedException e) {
				} catch (JoystickException e) {}
			}
		});
		updateThread.start();
	}
	
	public void setJoystickNum(int num) throws JoystickException {
		joystick = new Joystick("\\\\.\\PPJoyIOCTL" + num);
		joystickNum = num;
	}
	
	public int getJoystickNum() {
		return joystickNum;
	}
	
	public void setLimites(float x, float y) {
		if (x > 0 && y > 0) {
			limiteX = x;
			limiteY = y;
			factorX = (16384/limiteX);
			factorY = (16384/limiteY);
		}
	}
	
	public void setAnguloLimite(int angulo) {
		if (angulo > 0) {
			anguloLimite = angulo;
			factorAngular = (16384/anguloLimite);
		}
	}
	
	public void setDeadzones(float x, float y) {
		if (x >= 0 && y >= 0) {
			deadzoneX = x;
			deadzoneY = y;
		}
	}
	
	public void setUpdateInterval(int updateInterval) {
		if (updateInterval >= 0)
			this.updateInterval = updateInterval;
	}
	
	public int getUpdateInterval() {
		return updateInterval;
	}

	public int getAnguloLimite() {
		return anguloLimite;
	}

	public float getLimiteX() {
		return limiteX;
	}

	public float getLimiteY() {
		return limiteY;
	}

	public float getDeadzoneX() {
		return deadzoneX;
	}

	public float getDeadzoneY() {
		return deadzoneY;
	}

	@Override
	public void onViewRegistered(AndroidDevice device, View view) {
		if (view.getName().equals("acelerador"))
			acelerador = view;
		else if (view.getName().equals("freno"))
			freno = view;
		else if (view.getName().equals("arriba"))
			arriba = view;
		else if (view.getName().equals("abajo"))
			abajo = view;
		else if (view.getName().indexOf("button") != -1) {
			int boton = Integer.parseInt(view.getName().substring(6));
			botones[boton - 1] = view;
		}
	}

	public void desconectar() throws JoystickException, IOException {
		updateThread.interrupt();
		joystick.close();
		
		try {
			device.getAccelerometer().removeAccelerometerListener(this);
			device.getTouchScreen().removeViewListener(this);
			for (View v : device.getTouchScreen().getViews())
				v.removeOnTouchListener(this);
		} catch (NotConnectedException e) {}
	}
	
	@Override
	public void onTouch(View v, TouchEvent e) {
		int boton = -1;
		if(v.getName().equals("acelerador"))
			boton = 0;
		else if (v.getName().equals("freno"))
			boton = 1;
		else if (v.getName().equals("abajo"))
			boton = 2;
		else if (v.getName().equals("arriba"))
			boton = 3;
		else if (v.getName().indexOf("button") != -1)
			boton = Integer.parseInt(v.getName().substring(6)) - 1;
		
		if(joystick.digital[boton] != Joystick.DIGITAL_ON && e.getAction() == TouchEvent.ACTION_DOWN)
			joystick.digital[boton] = Joystick.DIGITAL_ON;
		else if(joystick.digital[boton] != Joystick.DIGITAL_OFF && (e.getAction() == TouchEvent.ACTION_UP || e.getAction() == TouchEvent.ACTION_OUTSIDE || e.getAction() == TouchEvent.ACTION_CANCEL))
			joystick.digital[boton] = Joystick.DIGITAL_OFF;
	}
	
	
	@Override
	public void onSensorChanged(AndroidDevice device, AccelerometerEvent event) {
		float x = event.getY();
		float y = event.getX();
		
		if (x > -deadzoneX && x < deadzoneX)
			x = 0;
		else if (x < -deadzoneX)
			x += deadzoneX;
		else if (x > deadzoneX)
			x -= deadzoneX;
		
		if (y > -deadzoneY && y < deadzoneY)
			y = 0;
		else if (y < -deadzoneY)
			y += deadzoneY;
		else if (y > deadzoneY)
			y -= deadzoneY;

		if (modo.equals("gamepad")) {			
			x = x * factorX + Joystick.ANALOG_MID;
			y = y * factorY + Joystick.ANALOG_MID;
				
			if (x < Joystick.ANALOG_MIN)
				x = Joystick.ANALOG_MIN;
			if (x > Joystick.ANALOG_MAX)
				x = Joystick.ANALOG_MAX;
			if (y < Joystick.ANALOG_MIN)
				y = Joystick.ANALOG_MIN;
			if (y > Joystick.ANALOG_MAX)
				y = Joystick.ANALOG_MAX;
			
			joystick.analog[Joystick.ANALOG_AXIS_X] = (int)x;
			joystick.analog[Joystick.ANALOG_AXIS_Y] = (int)y;
		
		} else if (modo.equals("wheel")) {
			
			double angle = Math.atan2(x, y) * 180 / Math.PI;

			if (angle < -anguloLimite)
				angle = -anguloLimite;
			if (angle > anguloLimite)
				angle = anguloLimite;
			
			if (angle >= anguloLimite && anguloAnterior <= -anguloLimite)
				angle = -anguloLimite;
			if (angle <= -anguloLimite && anguloAnterior >= anguloLimite)
				angle = anguloLimite;
			
			anguloAnterior = angle;
			
			angle = angle * factorAngular + Joystick.ANALOG_MID;
			
			if (angle < Joystick.ANALOG_MIN)
				angle = Joystick.ANALOG_MIN;
			if (angle > Joystick.ANALOG_MAX)
				angle = Joystick.ANALOG_MAX;
						
			joystick.analog[Joystick.ANALOG_AXIS_X] = (int) angle;
		}
	}
	
	public void customData(String text, Serializable object) throws IOException {
		if(text.equals("modo")){
			joystick.resetButtons();
			
			if(((String)object).equals("gamepad")){
				modo = "gamepad";
				device.getAccelerometer().setAccelerometerRate(Accelerometer.SENSOR_DELAY_FASTEST);
				device.getAccelerometer().addAccelerometerListener(this);
				for (int i = 0; i < 10; i++)
					if (botones[i] != null)
						botones[i].addOnTouchListener(this);
			} else if (((String)object).equals("wheel")) {
				modo = "wheel";
				device.getAccelerometer().setAccelerometerRate(Accelerometer.SENSOR_DELAY_FASTEST);
				device.getAccelerometer().addAccelerometerListener(this);
				if (acelerador != null) acelerador.addOnTouchListener(this);
				if (freno != null) freno.addOnTouchListener(this);
				if (arriba != null) arriba.addOnTouchListener(this);
				if (abajo != null) abajo.addOnTouchListener(this);
				if (botones[4] != null) botones[4].addOnTouchListener(this);
				if (botones[5] != null) botones[5].addOnTouchListener(this);
			}			
		} else if (text.equals("volver a") && ((String)object).equals("modo")){
			device.getAccelerometer().removeAccelerometerListener(this);
			for(View v : device.getTouchScreen().getViews())
				v.removeOnTouchListener(this);
			acelerador= null;
			freno = null;
			arriba = null;
			abajo = null;
			botones = new View[10];
			joystick.resetButtons();
		} else if (text.equals("menu") && ((String)object).equals("start")) {
			joystick.digital[10] = Joystick.DIGITAL_ON;
			new Timer().schedule(new TimerTask() {
				@Override
				public void run() {
					joystick.digital[10] = Joystick.DIGITAL_OFF;
				}
			}, 300);
		} else if (text.equals("menu") && ((String)object).equals("select")) {
			joystick.digital[11] = Joystick.DIGITAL_ON;
			new Timer().schedule(new TimerTask() {
				@Override
				public void run() {
					joystick.digital[11] = Joystick.DIGITAL_OFF;
				}
			}, 300);
		}
	}


}
